/*
 * Copyright (C) 2009, 2010, B3log Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.b3log.nide.service;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.b3log.nide.Nide;
import org.b3log.nide.model.NideFile;
import org.b3log.nide.model.Project;
import org.b3log.nide.model.ProjectSpace;
import org.b3log.latke.service.ServiceException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Project manager.
 *
 * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
 * @version 1.0.1.2, Nov 29, 2010
 */
public final class ProjectManager {

    /**
     * Logger.
     */
    private static final Logger LOGGER =
            Logger.getLogger(ProjectManager.class.getName());
    /**
     * Instance.
     */
    public static final ProjectManager INSTANCE = new ProjectManager();

    /**
     * Private default constructor.
     */
    private ProjectManager() {
    }

    /**
     * Gets a file by the specified file path.
     *
     * @param filePath the specified file path
     * @return for example:
     * <pre>
     * {
     *     "fileName": "name",
     *     "fileContent": "content"
     * }
     * </pre>
     * @throws ServiceException service exception
     */
    public JSONObject getFile(final String filePath) throws ServiceException {
        final JSONObject ret = new JSONObject();
        String content = null;

        final File file = new File(ProjectManager.getProjectsDir() + filePath);
        final String name = file.getName();

        try {
            content = IOUtils.toString(new FileReader(file));
        } catch (final IOException e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            throw new ServiceException(e);
        }

        try {
            ret.put(NideFile.FILE_NAME, name);
            ret.put(NideFile.FILE_CONTENT, content);

            LOGGER.log(Level.FINER, "Got file[{0}]", ret.toString(Nide.INDENT));
        } catch (final JSONException e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            throw new ServiceException(e);
        }

        return ret;
    }

    /**
     * Saves a file by the specified file path and file content.
     *
     * @param filePath the specified file path
     * @param fileContent the specified file content
     * @return for example:
     * <pre>
     * {
     * TODO
     * }
     * </pre>
     * @throws ServiceException service exception
     */
    public JSONObject saveFile(final String filePath, final String fileContent)
            throws ServiceException {
        final JSONObject ret = new JSONObject();

        final String fullPath = ProjectManager.getProjectsDir() + filePath;

        try {
            IOUtils.write(fileContent, new FileWriter(fullPath));
        } catch (final IOException e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            throw new ServiceException(e);
        }

        LOGGER.log(Level.FINE, "Saved file[filePath={0}, fileContent={1}]",
                   new Object[]{filePath, fileContent});

        return ret;
    }

    /**
     * Gets project space.
     *
     * @return projects space, for example:
     * <pre>
     * {
     *     "projectSpaceDir": "/home/daniel/nide/projects/"
     *     "projectSpaceProjectNameList": [
     *         "nide-test", "b3log", ....
     *     ]
     * }
     * </pre>
     * @throws ServiceException service exception
     */
    public JSONObject getProjectSpace() throws ServiceException {
        final JSONObject ret = new JSONObject();

        try {
            final String projectsDirPath = getProjectsDir();
            ret.put(ProjectSpace.PROJECT_SPACE_DIR, projectsDirPath);

            final File projectsDir = new File(projectsDirPath);
            final String[] projectNames = projectsDir.list();
            final JSONArray projectNameList = org.b3log.latke.util.CollectionUtils.
                    listToJSONArray(Arrays.asList(projectNames));

            ret.put(ProjectSpace.PROJECT_SPACE_PROJECT_NAME_LIST,
                    projectNameList);
        } catch (final JSONException e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            throw new ServiceException(e);
        }

        return ret;
    }

    /**
     * Gets the directory path of projects on server.
     *
     * @return the directory path of projects on server
     */
    public static String getProjectsDir() {
        return Nide.getNideProperties().getString("projects.dir");
    }

    /**
     * Generates a project space tree by the specified project space directory
     * path.
     * <p>
     * The data structure of the files tree refers to
     * <a href="http://code.google.com/p/jstree">jsTree.v.1.0rc</a>.
     * </p>
     *
     * @param projectSpaceDirPath the specified project space directory path
     * @return a generated project space tree
     * @throws ServiceException service exception
     */
    public JSONObject genProjectSpaceTree(final String projectSpaceDirPath)
            throws ServiceException {
        final JSONObject ret = new JSONObject();

        try {
            final JSONObject attr = new JSONObject();
            ret.put("attr", attr);
            attr.put("rel", ProjectSpace.PROJECT_SPACE);
            attr.put("id", ProjectSpace.PROJECT_SPACE);

            ret.put("data", "Projects");

            final JSONArray projects = new JSONArray();
            ret.put("children", projects);

            final File[] projectDirs = new File(projectSpaceDirPath).listFiles();
            LOGGER.fine("Listing projects....");
            for (int i = 0; i < projectDirs.length; i++) {
                final File projectDir = projectDirs[i];
                LOGGER.log(Level.FINE, "Project[name={0}]", projectDir.getName());
                final JSONObject projectNode = new JSONObject();

                final JSONObject projectAttr = new JSONObject();
                projectNode.put("attr", projectAttr);
                projectAttr.put("rel", Project.PROJECT);

                genFilesTree(projectDir, projectNode);
                projects.put(projectNode);
            }
        } catch (final JSONException e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            throw new ServiceException(e);
        }

        return ret;
    }

    /**
     * Generates a files tree by the specified root directory.
     *
     * @param root the specified root directory
     * @param rootNode generated tree node
     * @throws JSONException json exception
     */
    private void genFilesTree(final File root,
                              final JSONObject rootNode)
            throws JSONException {
        rootNode.put("data", root.getName());

        final File[] files = root.listFiles();
        final JSONArray nodes = new JSONArray();
        rootNode.put("children", nodes);

        for (int i = 0; i < files.length; i++) {
            final File file = files[i];

            final String fileName = file.getName();
            // XXX: ignores vcs files
            if (file.isHidden() || fileName.startsWith(".")) {
                continue;
            }

            final JSONObject node = new JSONObject();
            nodes.put(node);

            final JSONObject attr = new JSONObject();
            node.put("attr", attr);
            if (file.isDirectory()) {
                attr.put("rel", "folder");
                LOGGER.log(Level.FINER, "Loaded folder[{0}]", file.getPath());
                genFilesTree(file, node);
            } else {
                attr.put("rel", "file");
                LOGGER.log(Level.FINER, "Loaded file[{0}]", file.getPath());
                node.put("data", file.getName());
            }
        }
    }
}
